﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Drawing.Imaging;

using AForge;
using AForge.Imaging;
using AForge.Imaging.Filters;
using AForge.Video;
using AForge.Video.DirectShow;

namespace MultiColorTracking
{
    public partial class MultiObjectTracking : Form
    {
        // list of video devices
        private FilterInfoCollection videoDevices;

        ColorFiltering colorFilter = new ColorFiltering();
        GrayscaleBT709 grayFilter = new GrayscaleBT709();
        
        // use two blob counters, so the could run in parallel in two threads
        BlobCounter blobCounter = new BlobCounter();

        // object coordinates
        private float x1, y1;
        Point coor;
        Point emptiness = new Point(-1, -1);

        Dictionary<int, Point> points = new Dictionary<int, Point>();
        int captureNo = 0;

        private void SavePoint(Dictionary<int, Point> points, Point p)
        {
            lock (this)
            {
                captureNo++;
                if (p.X != emptiness.X && p.Y != emptiness.Y)
                {
                    points[captureNo] = p;
                }
            }
        }

        public MultiObjectTracking()
        {
            InitializeComponent();

            btnDisconnect.Enabled = false;

            try
            {
                // enumerate video devices
                videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);

                if (videoDevices.Count == 0)
                {
                    throw new Exception();
                }

                for (int i = 1, n = videoDevices.Count; i <= n; i++)
                {
                    string cameraName = i + " : " + videoDevices[i - 1].Name;

                    cbCameraList.Items.Add(cameraName);
                }
                cbCameraList.SelectedIndex = 0;
            }
            catch
            {
                btnConnect.Enabled = false;
                cbCameraList.Items.Add("No camera found !");
                cbCameraList.SelectedIndex = 0;
                cbCameraList.Enabled = false;
                connectToolStripMenuItem.Enabled = false;
                disconnectToolStripMenuItem.Enabled = false;
            }

            // Process LED RGB range in my room -> R:180,255 / G:0,120 / B:0,120
            colorFilter.Red = new IntRange(Convert.ToInt32(nudRedMin.Value), Convert.ToInt32(nudRedMax.Value));
            colorFilter.Green = new IntRange(Convert.ToInt32(nudGreenMin.Value), Convert.ToInt32(nudGreenMax.Value));
            colorFilter.Blue = new IntRange(Convert.ToInt32(nudBlueMin.Value), Convert.ToInt32(nudBlueMax.Value));

            blobCounter.MinWidth = 15;
            blobCounter.MinHeight = 15;
            blobCounter.FilterBlobs = true;
            blobCounter.ObjectsOrder = ObjectsOrder.Size;

            tbBlobMinWidth.Text = blobCounter.MinWidth.ToString();
            tbBlobMinHeight.Text = blobCounter.MinHeight.ToString();

            coor = emptiness;
        }

        private void Disconnect()
        {
            videoSourcePlayer1.SignalToStop();
            videoSourcePlayer1.WaitForStop();
            UpdateObjectPicture(null);
        }

        private void Connect()
        {
            if (cbCameraList.Items[0].ToString() != "No camera found !")
            {
                Disconnect();
                points.Clear();

                VideoCaptureDevice videoSource =
                    new VideoCaptureDevice(videoDevices[cbCameraList.SelectedIndex].MonikerString);

                videoSource.DesiredFrameSize = new Size(320, 240);
                videoSource.DesiredFrameRate = 20;

                videoSourcePlayer1.VideoSource = videoSource;
                videoSourcePlayer1.Start();
            }
        }

        private void videoSourcePlayer1_NewFrame( object sender, ref Bitmap image )
        {
            Bitmap objectImage = colorFilter.Apply(image);

            // lock image for further processing
            BitmapData objectData = objectImage.LockBits(
                new Rectangle(0, 0, image.Width, image.Height),
                ImageLockMode.ReadOnly, image.PixelFormat);

            // grayscaling
            UnmanagedImage grayImage = grayFilter.Apply(new UnmanagedImage(objectData));

            // unlock image
            objectImage.UnlockBits(objectData);

            // locate blobs 
            blobCounter.ProcessImage(grayImage);
            Rectangle[] rects = blobCounter.GetObjectsRectangles();

            if (rects.Length > 0)
            {
                for (int i = 0; i < rects.Length; i++)
                {
                    Rectangle objectRect = rects[i];

                    // draw rectangle around derected object
                    Graphics g = Graphics.FromImage(image);

                    using (Pen pen = new Pen(Color.FromArgb(32, 255, 32), 2))
                    {
                        g.DrawRectangle(pen, objectRect);
                    }

                     //get object's center coordinates relative to image center
                    lock (this)
                    {
                        x1 = Math.Abs(objectRect.Left + objectRect.Right - objectRect.Width) / 2;
                        y1 = Math.Abs(objectRect.Top + objectRect.Bottom - objectRect.Height) / 2;

                        coor = new Point(Convert.ToInt32(x1), Convert.ToInt32(y1));

                        g.DrawString(coor.ToString(),
                            new Font("Arial", 8, FontStyle.Bold), Brushes.White,
                            new Point(Convert.ToInt32(x1), Convert.ToInt32(y1) + Math.Abs(objectRect.Height)));
                    }

                    g.Dispose();
                }
            }
            else
            {
                coor = emptiness;
            }

            SavePoint(points, coor);
            UpdateObjectPicture(objectImage);
        }

        // Update object's picture
        public void UpdateObjectPicture(Bitmap picture)
        {
            System.Drawing.Image oldPicture = null;

            oldPicture = pictureBox1.Image;
            pictureBox1.Image = picture;

            if (oldPicture != null)
            {
                oldPicture.Dispose();
            }
        }

        private void SingleColorTracking_FormClosing(object sender, FormClosingEventArgs e)
        {
            Disconnect();
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            Connect();
            btnConnect.Enabled = false;
            btnDisconnect.Enabled = true;
            timerCoordReader.Enabled = true;

            tbCoordinates.Clear();
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            Disconnect();
            btnConnect.Enabled = true;
            btnDisconnect.Enabled = false;
            timerCoordReader.Enabled = false;

            string s = "";
            foreach (int t in points.Keys)
            {
                s += t.ToString() + ": " + points[t].ToString() + "\r\n";
            }
            tbCoordinates.Text = s;
        }

        private void nudRedMin_ValueChanged(object sender, EventArgs e)
        {
            colorFilter.Red = new IntRange(
                Convert.ToInt32(nudRedMin.Value),
                Convert.ToInt32(nudRedMax.Value));
        }

        private void nudRedMax_ValueChanged(object sender, EventArgs e)
        {
            colorFilter.Red = new IntRange(
                Convert.ToInt32(nudRedMin.Value),
                Convert.ToInt32(nudRedMax.Value));
        }

        private void nudGreenMin_ValueChanged(object sender, EventArgs e)
        {
            colorFilter.Green = new IntRange(
                Convert.ToInt32(nudGreenMin.Value),
                Convert.ToInt32(nudGreenMax.Value));

        }

        private void nudGreenMax_ValueChanged(object sender, EventArgs e)
        {
            colorFilter.Green = new IntRange(
                Convert.ToInt32(nudGreenMin.Value),
                Convert.ToInt32(nudGreenMax.Value));

        }

        private void nudBlueMin_ValueChanged(object sender, EventArgs e)
        {
            colorFilter.Blue = new IntRange(
                Convert.ToInt32(nudBlueMin.Value),
                Convert.ToInt32(nudBlueMax.Value));

        }

        private void nudBlueMax_ValueChanged(object sender, EventArgs e)
        {
            colorFilter.Blue = new IntRange(
                Convert.ToInt32(nudBlueMin.Value),
                Convert.ToInt32(nudBlueMax.Value));

        }

        private void tbBlobMinWidth_TextChanged(object sender, EventArgs e)
        {
            if (tbBlobMinWidth.Text != "")
                blobCounter.MinWidth = Convert.ToInt32(tbBlobMinWidth.Text);
        }

        private void tbBlobMinHeight_TextChanged(object sender, EventArgs e)
        {
            if (tbBlobMinHeight.Text != "")
                blobCounter.MinHeight = Convert.ToInt32(tbBlobMinHeight.Text);
        }

        private void timerCoordReader_Tick(object sender, EventArgs e)
        {
            //if (coor.X != emptiness.X && coor.Y != emptiness.Y)
            //    tbCoordinates.Text += coor.ToString() + "    ";
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void connectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Connect();
            btnConnect.Enabled = false;
            btnDisconnect.Enabled = true;
            timerCoordReader.Enabled = true;

            tbCoordinates.Clear();
        }

        private void disconnectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Disconnect();
            btnConnect.Enabled = true;
            btnDisconnect.Enabled = false;
            timerCoordReader.Enabled = false;

            string s = "";
            foreach (int t in points.Keys)
            {
                s += t.ToString() + ": " + points[t].ToString() + "\r\n";
            }
            tbCoordinates.Text = s;
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Developed by:\n\nFerhat İ. Balbozan\nN. Engin Toklu\nOnur Keskin", "About", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

    }
}
